home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mac-Source 1994 July
/
Mac-Source_July_1994.iso
/
C and C++
/
Libraries
/
TurboTCP 1.0.1
/
TurboTCP.source
/
CTCPStream.cp
< prev
next >
Wrap
Text File
|
1993-12-10
|
41KB
|
1,723 lines
/*
** CTCPStream.cp
**
** TurboTCP support library
** TCP stream class
**
** Copyright © 1993, FrostByte Design / Eric Scouten
**
*/
#include "CTCPStream.h"
#ifndef TurboTCPHeaders
#include <TCLUtilities.h>
#include <CList.h>
#include "TurboTCP.const.h"
#endif
#include "CTCPAsyncCall.h"
#include "CTCPDriver.h"
// —— initialize/destroy stream ——
/*______________________________________________________________________
**
** ITCPStream
**
** Create a new TCP stream. Allocates the receive buffer (if there’s enough memory).
**
** The stream can be configured to automatically receive data. If auto-receive is used,
** the stream object will automatically issue TCPNoCopyRcv calls as needed and respond to
** completion notifications when data arrives; data will be returned by the HandleDataArrived
** method or by the receive bypass procedure (see InstallRcvBypassProc).
**
** If auto-receive is used, you should not send NoCopyRcv and Rcv messages to this stream.
** They may conflict with the auto-receive calls. Auto-receive cannot be turned on or off
** once the stream object is created.
**
** recBufferSize (long): size of the receive buffer we need; use 0 or
** less then 4K to get the minimum 4K buffer
** autoReceiveSize (short): number of entries in RDS for auto-receive,
** 0 to disable autoreceiving
** autoReceiveNum (short): number of auto-receive calls to issue at once
** must be at least 1
**
*/
void CTCPStream::ITCPStream (long recBufferSize, short autoReceiveSize, short autoReceiveNum)
{
// clear all of our variables
macTCPStream = NULL;
itsBuffer = NULL;
itsAsyncCalls = NULL;
hasSessionOpen = pendingOpen = pendingClose = remoteClose
= pendingAbort = pendingNotify = pendingDispose = FALSE;
itsBufferSize = 0L;
rcvUrgent = FALSE;
itsRcvBypassProc = NULL;
itsRcvBypassTarget = NULL;
itsULPtimeout = 0;
itsULPaction = 0;
itsValidityFlag = 0;
itsCommandTimeoutValue = 0;
itsTosFlags = 0;
itsPrecedence = 0;
itsDontFrag = 0;
itsTimeToLive = 0;
itsSecurity = 0;
itsOptionCnt = 0;
sendNextUrgent = 0;
sendNextPush = FALSE;
notifClosing = FALSE;
notifTimeout = FALSE;
notifTerminate = FALSE;
notifDataArrived = FALSE;
notifUrgent = FALSE;
notifICMP = FALSE;
disposeOnTerminate = FALSE;
receivedClose = FALSE;
receivedTerminate = FALSE;
itsAutoReceiveSize = Min(autoReceiveSize, autoReceiveMax);
itsAutoReceiveNum = Max(autoReceiveNum, 1);
// prepare the async events queues
qNotifyEntry.qType = notifyStream;
qNotifyEntry.qSelfLink = this;
qDisposeEntry.qType = disposeStream;
qDisposeEntry.qSelfLink = this;
// initialize the collaborator object
CCollaborator::ICollaborator();
// create the async calls list
itsAsyncCalls = new (CCluster);
itsAsyncCalls->ICluster();
// create the receive buffer
if (recBufferSize < minReceiveSize) // if caller requested a buffer that is too small,
recBufferSize = minReceiveSize; // beef it up to acceptable size (4K)
itsBuffer = NewHandle(recBufferSize);
FailNIL(itsBuffer);
itsBufferSize = recBufferSize;
/*
** Don’t create TCP stream here. Therefore, if a session is never opened and the program
** crashes (or ExitToShell is used by the programmer), MacTCP will remain stable.
*/
}
/*______________________________________________________________________
**
** Dispose
**
** Get rid of a TCP stream. Deallocates the receive buffer. If a session is still open,
** issues an Abort command, then postpones its disposal so completion routines still
** have a valid object to call.
**
*/
void CTCPStream::Dispose (void)
{
TCPiopb theReleaseParam;
// we’re no longer in delayed disposal queue
pendingDispose = FALSE;
// initiate a graceful close if necessary
if (hasSessionOpen && !receivedClose) {
Close();
disposeOnTerminate = TRUE;
return;
}
// abort any connections that are yet to open
if (pendingOpen) {
Abort();
disposeOnTerminate = TRUE;
return;
}
// if calls are still outstanding, wait a while
if (!itsAsyncCalls->IsEmpty()) {
if ((macTCPStream) && pendingOpen) { // get rid of the thing…
Abort();
PostponeDispose();
return;
}
if ((macTCPStream) && pendingOpen)
DoSyncCall(TCPRelease, &theReleaseParam); // perhaps this will shake it loose
PostponeDispose();
return;
}
// if connection is still there, release stream to see if that shakes things loose
if (macTCPStream && hasSessionOpen && !receivedTerminate) {
DoSyncCall(TCPRelease, &theReleaseParam);
macTCPStream = NULL;
PostponeDispose();
return;
}
// make sure that terminate notice has been received
if (hasSessionOpen && !receivedTerminate) {
PostponeDispose();
return;
}
// release the stream from MacTCP
if (macTCPStream)
DoSyncCall(TCPRelease, &theReleaseParam);
gTCPDriver->RemoveActiveStream(this);
// forget the async calls list
ForgetObject(itsAsyncCalls);
// release the receive buffer
ForgetHandle(itsBuffer);
macTCPStream = NULL;
itsBufferSize = 0L;
CCollaborator::Dispose();
}
/*______________________________________________________________________
**
** OwnerDied
**
** Called when stream’s owner is being disposed and no longer wishes to receive
** notifications.
**
*/
void CTCPStream::OwnerDied (void)
{
InstallRcvBypass(NULL, NULL);
}
// —— basic user TCP calls ——
/*______________________________________________________________________
**
** OpenConnection
**
** Opens a TCP session (either passive or active). Note that MacTCP seems to fail when
** non-zero values are used for theRemoteIP and theRemotePort.
**
** passive (Boolean): TRUE for passive open; FALSE for active open
** theRemoteIP (ip_addr): IP address of remote host (0 for any host)
** theRemotePort (b_16): TCP port of remote host (0 for any port)
** theLocalPort (b_16): TCP port for local host (0 for any port)
**
*/
void CTCPStream::OpenConnection (Boolean passive, ip_addr theRemoteIP,
b_16 theRemotePort, b_16 theLocalPort)
{
TCPiopb theCreateParam;
TCPiopb theOpenParam;
OSErr theResult;
// ensure that MacTCP is awake & ready for us
if (!(gTCPDriver->CheckTCPDriver()))
FailOSErr(noTCPError);
// if the stream hasn’t been created yet, create one now
if (!macTCPStream) {
MoveHHi(itsBuffer); // lock the receive buffer
HLock(itsBuffer);
MoveHHi((Handle) this); // prevent ASR calls while Memory Manager
this->Lock(TRUE); // is moving this object
theCreateParam.csParam.create.rcvBuff = *itsBuffer;
theCreateParam.csParam.create.rcvBuffLen = itsBufferSize;
theCreateParam.csParam.create.notifyProc = &NotifyProc;
theCreateParam.csParam.create.userDataPtr = (Ptr) this;
macTCPStream = (Ptr) 1; // preempt the check for no stream present
theResult = DoSyncCall(TCPCreate, &theCreateParam);
if (theResult == noErr) { // did we get a valid stream?
gTCPDriver->RegisterActiveStream(this); // yes, register in global stream list
macTCPStream = (Ptr) theCreateParam.tcpStream;
} else {
macTCPStream = NULL;
HandleOpenFailed(theResult);
}
}
// don’t allow open if already opening
if (!(hasSessionOpen || pendingOpen)) {
// fill in parms to open call
theOpenParam.csParam.open.ulpTimeoutValue = itsULPtimeout;
theOpenParam.csParam.open.ulpTimeoutAction = itsULPaction;
theOpenParam.csParam.open.validityFlags = itsValidityFlag;
theOpenParam.csParam.open.commandTimeoutValue = itsCommandTimeoutValue;
theOpenParam.csParam.open.remoteHost = theRemoteIP;
theOpenParam.csParam.open.remotePort = theRemotePort;
theOpenParam.csParam.open.localPort = theLocalPort;
theOpenParam.csParam.open.tosFlags = itsTosFlags;
theOpenParam.csParam.open.precedence = itsPrecedence;
theOpenParam.csParam.open.dontFrag = itsDontFrag;
theOpenParam.csParam.open.timeToLive = itsTimeToLive;
theOpenParam.csParam.open.security = itsSecurity;
theOpenParam.csParam.open.optionCnt = itsOptionCnt;
BlockMove(&itsOptions, &theOpenParam.csParam.open.options, 40);
theOpenParam.csParam.open.userDataPtr = (Ptr) this;
pendingOpen = TRUE;
receivedClose = receivedTerminate = FALSE;
// perform the call
theResult = DoAsyncCall((passive ? TCPPassiveOpen : TCPActiveOpen),
&theOpenParam);
if (theResult)
HandleOpenFailed(theResult);
// pull IP address/port info from remote host
itsRemoteIP = theOpenParam.csParam.open.remoteHost;
itsRemotePort = theOpenParam.csParam.open.remotePort;
itsLocalIP = theOpenParam.csParam.open.localHost;
itsLocalPort = theOpenParam.csParam.open.localPort;
}
else
HandleOpenFailed(connectionExists);
}
/*______________________________________________________________________
**
** Close
**
** Signal that the connection should be closed. The stream’s owner should wait until it
** receives the tcpStreamClosed message before disposing of the stream object.
** This will allow time for the host to close gracefully.
**
*/
void CTCPStream::Close (void)
{
TCPiopb theCloseParam;
OSErr theResult;
if ((hasSessionOpen && (!pendingAbort) && (!pendingClose)) || remoteClose) {
pendingClose = TRUE;
pendingOpen = remoteClose = FALSE;
theCloseParam.csParam.close.ulpTimeoutValue = itsULPtimeout;
theCloseParam.csParam.close.ulpTimeoutAction = itsULPaction;
theCloseParam.csParam.close.validityFlags = itsValidityFlag;
theCloseParam.csParam.close.userDataPtr = (Ptr) this;
theResult = DoAsyncCall(TCPClose, &theCloseParam);
if ((theResult != noErr) && (theResult != connectionDoesntExist))
HandleTCPError(theResult, TCPClose);
}
}
/*______________________________________________________________________
**
** Abort
**
** Cancel the connection immediately. This call is performed synchronously. USE THIS CALL
** WITH CAUTION! MacTCP seems to get quite confused when a session is closed and
** data hasn’t all been received.
**
*/
void CTCPStream::Abort (void)
{
TCPiopb theAbortParam;
OSErr theResult;
if ((hasSessionOpen || pendingOpen) && (!pendingAbort)) {
pendingAbort = TRUE;
pendingOpen = pendingClose = remoteClose = FALSE;
theAbortParam.csParam.abort.userDataPtr = (Ptr) this;
theResult = DoSyncCall(TCPAbort, &theAbortParam);
if (theResult)
HandleTCPError(theResult, TCPAbort);
else
receivedClose = TRUE;
}
}
/*______________________________________________________________________
**
** NoCopyRcv
**
** Receive data without copying from TCP’s internal buffers. The completion routine in
** CTCPAsyncCall will take care of returning the RDS automatically. IF YOU ARE USING
** AUTO-RECEIVE, DO NOT CALL THIS METHOD!
**
** itsRDS (rdsEntry *): receive data structure (see MacTCP manual, p30)
** itsRDSSize (b_16): number of entries in RDS (6 bytes each)
** itsTimeOut (b_16): command timeout value in seconds (0 = infinite)
**
*/
void CTCPStream::NoCopyRcv (rdsEntry *itsRDS, b_16 itsRDSSize, b_16 itsTimeOut)
{
TCPiopb theRcvParam;
OSErr theResult;
theRcvParam.csParam.receive.commandTimeoutValue = itsTimeOut;
theRcvParam.csParam.receive.rdsPtr = (Ptr) itsRDS;
theRcvParam.csParam.receive.rdsLength = (unsigned short) Min(itsRDSSize, autoReceiveMax);
theRcvParam.csParam.receive.userDataPtr = (Ptr) this;
theRcvParam.csParam.open.options[36] = (Byte) itsAutoReceiveSize;
theResult = DoAsyncCall(TCPNoCopyRcv, &theRcvParam);
if ((theResult != noErr) && (theResult != connectionClosing))
HandleTCPError(theResult, TCPNoCopyRcv);
}
/*______________________________________________________________________
**
** BfrReturn
**
** Return a receive buffer to MacTCP used by the NoCopyRcv method. You should never need
** to call this method, since it is done automatically by the CTCPAsyncCall::Dispatch()
** method which handles all asynchronous completions.
**
** itsRDS (Ptr): the RDS structure to return
**
*/
void CTCPStream::BfrReturn (Ptr itsRDS)
{
TCPiopb theRcvParam;
OSErr theResult;
// reject attempt to return buffers after the session is closed…
// MacTCP has done it already (I think)
if (hasSessionOpen) {
theRcvParam.csParam.receive.rdsPtr = itsRDS;
theRcvParam.csParam.receive.userDataPtr = (Ptr) this;
DoSyncCall(TCPRcvBfrReturn, &theRcvParam);
// ignore errors — this is bad practice, but…
}
}
/*______________________________________________________________________
**
** Send
**
** Send data on the TCP stream using the Write Data Structure (WDS) structure. The
** asynchronous completion routine can optionally dispose of the WDS and its associated data
** structures once the write operation is completed. If so, the buffers you provide must be
** expendable.
**
** itsWDS (wdsEntry *): write data structure (see MacTCP manual, p30)
** itsTimeOut (b_16): command timeout value in seconds (0 = infinite)
** disposeWDS (Boolean): dispose of WDS and data structures when completed
**
*/
void CTCPStream::Send (wdsEntry *itsWDS, b_16 itsTimeOut, Boolean disposeWDS)
{
TCPiopb theSendParam;
OSErr theResult;
// ensure that at least one byte is being sent
if ((*itsWDS).length == 0)
return;
// fill in parms to send call
theSendParam.csParam.send.ulpTimeoutValue = itsTimeOut;
theSendParam.csParam.send.ulpTimeoutAction = itsULPaction;
theSendParam.csParam.send.validityFlags = itsValidityFlag;
theSendParam.csParam.send.pushFlag = sendNextPush;
theSendParam.csParam.send.urgentFlag = sendNextUrgent;
theSendParam.csParam.send.wdsPtr = (Ptr) itsWDS;
theSendParam.csParam.send.userDataPtr = (Ptr) this;
theSendParam.csParam.open.security = disposeWDS;
// ^^^^ this isn’t pretty, but it’s efficient
sendNextPush = FALSE;
sendNextUrgent = 0;
// perform the call
theResult = DoAsyncCall(TCPSend, &theSendParam);
if (theResult)
HandleTCPError(theResult, TCPSend);
}
/*______________________________________________________________________
**
** Receive
**
** Receive data and copy to a user buffer.
**
** theData (Ptr): receive buffer
** theDataSize (b_16): size of receive buffer in bytes
** itsTimeOut (b_16): command timeout value in seconds (0 = infinite)
**
*/
void CTCPStream::Receive (Ptr theData, b_16 itsDataSize, b_16 itsTimeOut)
{
TCPiopb theRcvParam;
OSErr theResult;
theRcvParam.csParam.receive.commandTimeoutValue = itsTimeOut;
theRcvParam.csParam.receive.rcvBuff = theData;
theRcvParam.csParam.receive.rcvBuffLen = (unsigned short) itsDataSize;
theRcvParam.csParam.receive.userDataPtr = (Ptr) this;
theRcvParam.csParam.open.userDataPtr = (Ptr) itsRcvBypassProc;
// hide the extra data in unused fields
theResult = DoAsyncCall(TCPRcv, &theRcvParam);
if (theResult)
HandleTCPError(theResult, TCPRcv);
}
/*______________________________________________________________________
**
** Status
**
** Returns a large pile of information about the TCP connection.
**
** theStatusBlock (TCPStatusPB *): user buffer for TCP status info
**
*/
void CTCPStream::Status (TCPStatusPB *theStatusBlock)
{
TCPiopb theStatusParam;
short i;
Ptr p;
DoSyncCall(TCPStatus, &theStatusParam);
BlockMove(&theStatusParam.csParam, theStatusBlock, 66);
if (theStatusParam.ioResult != noErr) {
i = 0;
p = (Ptr) theStatusBlock;
while (i++<66)
*(p++) = 0;
}
}
/*______________________________________________________________________
**
** ConnectionState
**
** Returns the current status of the MacTCP stream. For definitions of the state codes,
** see TCP reference manual, p56.
**
** return (b_16): status of TCP stream
**
*/
b_16 CTCPStream::ConnectionState (void)
{
TCPStatusPB theStatusBlock;
Status(&theStatusBlock);
return(theStatusBlock.connectionState);
}
// —— specialized functions for sending data ——
/*______________________________________________________________________
**
** SendChar
**
** Send a single character to the TCP stream.
**
** theChar (char): the character to send
**
*/
void CTCPStream::SendChar (char theChar)
{
SendBfrCpy(&theChar, 1);
}
/*______________________________________________________________________
**
** SendCString
**
** Send a C string to the TCP stream.
**
** theString (char *): the string to send
**
*/
void CTCPStream::SendCString (char *theString)
{
SendBfrCpy(theString, cstrlen(theString));
}
/*______________________________________________________________________
**
** SendPString
**
** Send a Pascal string to the TCP stream.
**
** theString (Str255): the string to send
**
*/
void CTCPStream::SendPString (ConstStr255Param theString)
{
SendBfrCpy((char *) theString + 1, theString[0]);
}
/*______________________________________________________________________
**
** SendBfrCpy
**
** Send a range of bytes to the TCP stream. Prepares a WDS for the Send method and copies
** the bytes from the caller’s buffer to a data buffer which can persist beyond the procedure
** or object which generated the data.
**
** theData (Ptr): the bytes to send
** theDataSize (b_16): number of bytes to send
**
*/
void CTCPStream::SendBfrCpy (Ptr theData, b_16 theDataSize)
{
wdsEntry *itsWDS = NULL;
Ptr itsNewBuffer = NULL;
// make sure there’s something to send
if (theDataSize == 0)
return;
TRY {
// create the two buffers
itsWDS = (wdsEntry *) NewPtr(8); // WDS with room for one entry
itsNewBuffer = NewPtr(theDataSize);
// copy stuff to new buffer
BlockMove(theData, itsNewBuffer, theDataSize);
(*itsWDS).length = theDataSize;
(*itsWDS).ptr = itsNewBuffer;
(*(++itsWDS)).length = 0;
--itsWDS;
// send data, using default timeout value
Send(itsWDS, itsULPtimeout, TRUE);
}
CATCH {
ForgetPtr(itsWDS);
ForgetPtr(itsNewBuffer);
}
ENDTRY;
}
/*______________________________________________________________________
**
** SendBfrNoCpy
**
** Send a range of bytes to the TCP stream. Prepares a WDS for the Send method. Does not
** copy the bytes.
**
** theData (Ptr): the bytes to send
** theDataSize (b_16): number of bytes to send
** disposeWhenDone (Boolean): dispose of the buffer when completed — set to TRUE
** only if this pointer was allocated as a pointer,
** not a handle deref
**
*/
void CTCPStream::SendBfrNoCpy (Ptr theData, b_16 theDataSize, Boolean disposeWhenDone)
{
wdsEntry *itsWDS = NULL;
// make sure there’s something to send
if (theDataSize == 0)
return;
TRY {
// create the WDS buffer & fill it in
itsWDS = (wdsEntry *) NewPtr(8); // WDS with room for one entry
(*itsWDS).length = theDataSize;
(*itsWDS).ptr = theData;
(*(++itsWDS)).length = 0;
--itsWDS;
// send data, using default timeout value
Send(itsWDS, itsULPtimeout, disposeWhenDone);
}
CATCH {
ForgetPtr(itsWDS);
}
ENDTRY;
}
/*______________________________________________________________________
**
** SetNextPush
**
** Use before any send-data command to make sure that data is sent with the “push” flag set.
**
*/
void CTCPStream::SetNextPush (void)
{
sendNextPush = TRUE;
}
/*______________________________________________________________________
**
** SetNextUrgent
**
** Use before any send-data command to make sure that data is sent with the “urgent”
** flag set.
**
** useRFC793 (Boolean): TRUE to use the non-compliant method in RFC 793
**
*/
void CTCPStream::SetNextUrgent (Boolean useRFC793)
{
sendNextUrgent = (useRFC793 ? 2 : 1);
}
// —— notification routines
/*______________________________________________________________________
**
** HandleClosed
**
** Respond to notification that the session has closed.
**
*/
void CTCPStream::HandleClosed (void)
{
BroadcastSafeChange(tcpStreamClosed, NULL);
}
/*______________________________________________________________________
**
** HandleClosing
**
** Respond to notification that the session is being closed.
**
** remoteClosing (Boolean): TRUE if close initiated by remote host
**
*/
void CTCPStream::HandleClosing (Boolean remoteClosing)
{
BroadcastSafeChange(tcpStreamClosing, (void *) remoteClosing);
}
/*______________________________________________________________________
**
** HandleDataArrived (should only be called by CTCPAsyncCall::Dispatch)
**
** Process notification that data has arrived.
**
** theData (Ptr): pointer to the data
** theDataSize (b_16): size of data
** isUrgent (Boolean): TRUE if remote host signalled TCP urgent data
**
*/
void CTCPStream::HandleDataArrived (Ptr theData, b_16 theDataSize, Boolean isUrgent)
{
DataArrivedInfo info;
info.theData = theData;
info.theDataSize = theDataSize;
info.isUrgent = isUrgent;
BroadcastChange(tcpStreamDataArrived, &info);
}
/*______________________________________________________________________
**
** HandleDataSent
**
** Respond to notification that data has been successfully sent.
**
** WDSPtr (wdsEntry *): the WDS structure for the data that was sent
**
*/
void CTCPStream::HandleDataSent (wdsEntry *WDSPtr)
{
BroadcastChange(tcpStreamDataSent, WDSPtr);
}
/*______________________________________________________________________
**
** HandleICMP
**
** Respond to an ICMP message which was received.
**
** icmpMsg (struct ICMPReport *): the ICMP message report
**
*/
void CTCPStream::HandleICMP (struct ICMPReport *icmpMsg)
{
BroadcastChange(tcpStreamICMP, icmpMsg);
}
/*______________________________________________________________________
**
** HandleOpened
**
** Respond to successful completion of a TCPPassiveOpen or TCPActiveOpen command.
**
*/
void CTCPStream::HandleOpened (void)
{
short i;
// let everyone know we’ve got an active session
hasSessionOpen = TRUE;
pendingOpen = FALSE;
BroadcastChange(tcpStreamOpened, NULL);
// issue all the auto-receive commands needed
for (i=1; i<=itsAutoReceiveNum; i++)
IssueAutoReceive();
}
/*______________________________________________________________________
**
** HandleOpenFailed
**
** Respond to notification that a TCPOpen command failed.
**
** theResultCode (OSErr): the reason for failure
**
*/
void CTCPStream::HandleOpenFailed (OSErr theResultCode)
{
hasSessionOpen = FALSE;
pendingOpen = FALSE;
BroadcastSafeChange(tcpStreamOpenFailed, (void *) theResultCode);
}
/*______________________________________________________________________
**
** HandleSendFailed
**
** Respond to failure to send data. The WDS is returned so the data may be re-sent. After
** the dependents are notified, this method frees the WDS and the data buffers.
**
** WDSPtr (wdsEntry *): the WDS structure
** theResultCode (OSErr): the reason for failure
**
*/
void CTCPStream::HandleSendFailed (wdsEntry *WDSPtr, OSErr theResultCode)
{
SendFailedInfo info;
wdsEntry *theDisposePtr = WDSPtr;
// package up the information for dependent objects
info.WDSPtr = WDSPtr;
info.theResultCode = theResultCode;
BroadcastSafeChange(tcpStreamSendFailed, &info);
// free the buffers holding the unsent data
while ((*theDisposePtr).length) {
ForgetPtr((*theDisposePtr).ptr);
theDisposePtr++;
}
ForgetPtr(WDSPtr);
}
/*______________________________________________________________________
**
** HandleTCPError
**
** Respond to failure of a TCP command that was not expected. Sends notification to
** dependent objects.
**
** theResultCode (OSErr): the result code
** theCsCode (short): the TCP command number that failed
**
*/
void CTCPStream::HandleTCPError (OSErr theResultCode, short theCsCode)
{
TCPErrorInfo info;
info.theResultCode = theResultCode;
info.theCsCode = theCsCode;
BroadcastSafeChange(tcpStreamTCPError, &info);
}
/*______________________________________________________________________
**
** HandleTerminated
**
** Respond to session termination event.
**
** terminReason (b_16): the termination code
**
*/
void CTCPStream::HandleTerminated (b_16 terminReason)
{
TerminatedInfo info;
info.terminReason = terminReason;
info.aboutToDispose = pendingDispose || disposeOnTerminate;
BroadcastSafeChange(tcpStreamTerminated, &info);
}
/*______________________________________________________________________
**
** HandleTimeout
**
** Respond to a ULP timeout event (remote TCP failed to respond in time). This method is
** only called if the ULP timeout action is set to abort & notify.
**
*/
void CTCPStream::HandleTimeout (void)
{
BroadcastSafeChange(tcpStreamTimeout, NULL);
}
/*______________________________________________________________________
**
** HandleUnexpectedData
**
** Respond to notification that data arrived without a receive command.
**
*/
void CTCPStream::HandleUnexpectedData (void)
{
BroadcastChange(tcpStreamUnexpectedData, NULL);
}
/*______________________________________________________________________
**
** HandleUrgentBegin
**
** Respond to notification that urgent data is arriving.
**
*/
void CTCPStream::HandleUrgentBegin (void)
{
BroadcastChange(tcpStreamUrgentBegin, NULL);
}
// —— set configuration ——
/*______________________________________________________________________
**
** InstallRcvBypass
**
** Install a receive “bypass” routine. This routine may be used to accelerate the processing
** of arriving data by bypassing several method dispatches.
**
** A bypass routine is called by the CTCPAsyncCall::Dispatch routine for each block of data.
** This call is made in place of the call to CTCPStream::HandleDataArrived. The routine
** should make a class-specific call to your object’s routine for processing data with the
** same parameters provided to it. (See example below.)
**
** typedef void (*RcvBypassProc) (CDirectorOwner *theObject, Ptr theData,
** b_16 theDataSize, Boolean isUrgent);
**
** {
** ((CYourClass *) theObject)->HandleDataArrived(…);
** }
**
**
** input (to InstallRcvBypass):
** aRcvBypassProc (RcvBypassProc): the receive bypass procedure for this stream
**
*/
struct BypassTargetRec {
CObject *target;
RcvBypassProc proc;
};
void CTCPStream::InstallRcvBypass (CObject *whoToCall, RcvBypassProc aRcvBypassProc)
{
struct BypassTargetRec s;
s.target = itsRcvBypassTarget = whoToCall;
s.proc = itsRcvBypassProc = aRcvBypassProc;
itsAsyncCalls->DoForEach1(&GiveBypassToAsyncCall, (long) &s);
}
// private static method for iteration
void CTCPStream::GiveBypassToAsyncCall (CObject *theObject, long param)
{
((CTCPAsyncCall *) theObject)->
SetRcvBypassProc(((struct BypassTargetRec *) param)->target,
((struct BypassTargetRec *) param)->proc);
}
/*______________________________________________________________________
**
** SetULPTimeoutValue
**
** Sets the ULP timeout value. The value given here applies to all subsequent open and
** send calls.
**
** ulpTimeoutValue (b_16): timeout value (seconds, 0 = infinite)
**
*/
void CTCPStream::SetULPTimeoutValue (b_16 ulpTimeoutValue)
{
itsULPtimeout = ulpTimeoutValue;
itsValidityFlag |= timeoutValue;
}
/*______________________________________________________________________
**
** SetULPTimeoutAction
**
** Sets the ULP timeout value. This call is only useful when a connection has not been opened
** on this stream. (Otherwise, the value applies to subsequent connections on the same
** stream.)
**
** ulpTimeoutAction (b_16): ULP timeout action (0 = report, nonzero = abort)
**
*/
void CTCPStream::SetULPTimeoutAction (b_16 ulpTimeoutAction)
{
itsULPaction = ulpTimeoutAction;
itsValidityFlag |= timeoutAction;
}
/*______________________________________________________________________
**
** SetCommandTimeout
**
** Sets the command timeout value. This call is only useful when a connection has not been
** opened on this stream. (Otherwise, the value applies to subsequent connections on the
** same stream.)
**
** cmdTimeoutValue (b_16): command timeout value in seconds
**
*/
void CTCPStream::SetCommandTimeout (b_16 cmdTimeoutValue)
{
itsCommandTimeoutValue = cmdTimeoutValue;
}
/*______________________________________________________________________
**
** SetTypeOfService
**
** Sets the type of service flag. This call is only useful when a connection has not been
** opened on this stream. (Otherwise, the value applies to subsequent connections on the
** same stream.)
**
** newTosFlag (b_16): type-of-service value
**
*/
void CTCPStream::SetTypeOfService (b_16 newTosFlag)
{
itsTosFlags = newTosFlag;
itsValidityFlag |= typeOfService;
}
/*______________________________________________________________________
**
** SetPrecedence
**
** Sets the precedence flag. This call is only useful when a connection has not been opened on
** this stream. (Otherwise, the value applies to subsequent connections on the same stream.)
**
** newPrecedence (b_16): the precedence flag
**
*/
void CTCPStream::SetPrecedence (b_16 newPrecedence)
{
itsPrecedence = newPrecedence;
itsValidityFlag |= precedence;
}
/*______________________________________________________________________
**
** SetDontFrag
**
** Sets the don’t fragment flag. This call is only useful when a connection has not been
** opened on this stream. (Otherwise, the value applies to subsequent connections on the
** same stream.)
**
** newDontFrag (b_16): TRUE to prevent fragmentation of data segments
**
*/
void CTCPStream::SetDontFrag (b_16 newDontFrag)
{
itsDontFrag = newDontFrag;
}
/*______________________________________________________________________
**
** SetTimeToLive
**
** Sets the ULP timeout value. This call is only useful when a connection has not been
** opened on this stream. (Otherwise, the value applies to subsequent connections on the
** same stream.)
**
** newTimeToLive (b_16): time to live value (seconds)
**
*/
void CTCPStream::SetTimeToLive (b_16 newTimeToLive)
{
itsTimeToLive = newTimeToLive;
}
/*______________________________________________________________________
**
** SetSecurity
**
** Sets the security flag. This call is only useful when a connection has not been opened on
** this stream. (Otherwise, the value applies to subsequent connections on the same stream.)
**
** newSecurity (b_16): security flag value
**
*/
void CTCPStream::SetSecurity (b_16 newSecurity)
{
itsSecurity = newSecurity;
}
/*______________________________________________________________________
**
** SetIPOptions
**
** Sets the IP options. This call is only useful when a connection has not been opened on this
** stream. (Otherwise, the value applies to subsequent connections on the same stream.)
**
** newOptionsSize (b_16): number of bytes in IP options string
** newOptions (IPOptionString *): the IP options bytes
**
*/
void CTCPStream::SetIPOptions (b_16 newOptionsSize, IPOptionString *newOptions)
{
Boolean wasLocked = this->Lock(TRUE);
itsOptionCnt = newOptionsSize;
BlockMove(newOptions, &itsOptions, IPOptionStringSize);
this->Lock(wasLocked);
}
// —— TCP urgent mode ——
/*______________________________________________________________________
**
** RcvUrgentStatus
**
** Return urgent mode status of stream.
**
** return (Boolean): TRUE if stream is receiving urgent data
**
*/
Boolean CTCPStream::RcvUrgentStatus (void)
{
return(rcvUrgent);
}
/*______________________________________________________________________
**
** RcvUrgentBegin (should only be called by TCP completion routine)
**
** Signal beginning of urgent data.
**
*/
void CTCPStream::RcvUrgentBegin (void)
{
rcvUrgent = TRUE;
}
/*______________________________________________________________________
**
** RcvUrgentMark (should only be called by TCP completion routine)
**
** Signal end of urgent data.
**
*/
void CTCPStream::RcvUrgentMark (void)
{
rcvUrgent = FALSE;
}
// —— protected methods to issue TCP calls ——
/*______________________________________________________________________
**
** DoAsyncCall (protected method)
**
** Creates a CTCPAsyncCall object and uses it to execute a TCP Device Manager call
** asynchronously. Does not wait for completion. Fails if not enough memory to
** create call object.
**
** theCsCode (b_16): TCP operation code
** theParamBlockPtr (TCPiopb *): parameter block for TCP call
**
** return (OSErr): result code (+1 is not returned)
**
*/
OSErr CTCPStream::DoAsyncCall (b_16 theCsCode, TCPiopb *theParamBlockPtr)
{
OSErr theResult;
CTCPAsyncCall *theCall;
// make sure a stream was opened
if (macTCPStream)
(*theParamBlockPtr).tcpStream = (StreamPtr) macTCPStream;
else
return (invalidStreamPtr);
// create call object
theCall = new (CTCPAsyncCall);
theCall->ITCPAsyncCall(this);
if (theCsCode == TCPNoCopyRcv)
theCall->SetRcvBypassProc(itsRcvBypassTarget, itsRcvBypassProc);
theResult = theCall->DoAsyncCall(theCsCode, theParamBlockPtr);
if (theResult == inProgress)
itsAsyncCalls->Add(theCall);
return ((theResult == inProgress) ? noErr : theResult);
}
/*______________________________________________________________________
**
** DoSyncCall (protected method)
**
** Fills in the standard parameters for a TCP Device Manager call and executes the call.
** Waits for completion of the call.
**
** theCsCode (b_16): TCP operation code
** theParamBlockPtr (TCPiopb *): parameter block for TCP call
**
** return (OSErr): result code (+1 is not returned)
**
*/
OSErr CTCPStream::DoSyncCall (b_16 theCsCode, TCPiopb *theParamBlockPtr)
{
// make sure a stream was opened
if (macTCPStream)
(*theParamBlockPtr).tcpStream = (StreamPtr) macTCPStream;
else
return (invalidStreamPtr);
// perform the call
(*theParamBlockPtr).ioCompletion = (TCPIOCompletionProc) NULL;
(*theParamBlockPtr).ioCRefNum = gTCPDriver->GetTCPRefNum();
(*theParamBlockPtr).csCode = theCsCode;
PBControlSync((ParmBlkPtr) theParamBlockPtr);
return ((*theParamBlockPtr).ioResult);
}
// —— auto-receive processing ——
/*______________________________________________________________________
**
** IssueAutoReceive (protected method)
**
** Issue a NoCopyRcv command to grab the next batch of data. Does nothing if auto-receive
** is not enabled.
**
*/
void CTCPStream::IssueAutoReceive (void)
{
Ptr newRDS; // the new RDS pointer
// create a new RDS with room for # of entries requested & send it to TCP
if (hasSessionOpen && itsAutoReceiveSize) {
FailNIL(newRDS = NewPtr((itsAutoReceiveSize*6)+2));
NoCopyRcv((rdsEntry *) newRDS, itsAutoReceiveSize, 0);
}
}
// —— modified collaborator mechanism ——
/*______________________________________________________________________
**
** BroadcastSafeChange (protected method)
**
** Same as CCollaborator::BroadcastChange, except it iterates over a copy of the
** dependents list. This allows the dependents to dispose of themselves in the iteration
** routine without wreaking havoc.
**
** reason (long): the reason code to broadcast
** info (void *): additional information to pass along
**
*/
typedef struct tUpdateInfo {
CCollaborator *provider;
long reason;
void *info;
} tUpdateInfo;
void CTCPStream::BroadcastSafeChange (long reason, void *info)
{
tUpdateInfo updateInfo;
long nullInfo;
CList *itsDependentsCopy;
if (itsDependents) {
// clone the dependents list
itsDependentsCopy = (CList *) (itsDependents->Copy());
// create a valid dereference for NULL info
if (!info) {
nullInfo = 0L;
info = &nullInfo;
}
// iterate over the list
updateInfo.provider = this;
updateInfo.reason = reason;
updateInfo.info = info;
itsDependentsCopy->DoForEach1(CCollaborator::Dependent_Update,
(long) &updateInfo);
ForgetObject(itsDependentsCopy);
}
}
// —— delayed-processing routines: respond to interrupt notifications
/*______________________________________________________________________
**
** ProcessNotify
**
** Respond to notifications received by the ASR during interrupt time. This routine is free
** of interrupt-level constraints.
**
*/
void CTCPStream::ProcessNotify (void)
{
// no longer in notify queue
pendingNotify = FALSE;
// if terminated, inform the stream owner & dispose (if appropriate)
// clear all other notifications (they no longer apply)
if (notifTerminate) {
HandleTerminated(notifTermReason);
hasSessionOpen = pendingOpen = pendingClose =
remoteClose = pendingAbort = notifClosing = notifTimeout =
notifDataArrived = notifUrgent = FALSE;
receivedClose = receivedTerminate = TRUE;
if (disposeOnTerminate)
PostponeDispose();
}
// if closed, inform stream owner, wait for termination before disposing (if appropriate)
if (notifClosing) {
HandleClosing(remoteClose);
receivedClose = TRUE;
notifClosing = FALSE;
if (disposeOnTerminate)
PostponeDispose();
}
// if urgent notification, flag beginning of urgent data
if (notifUrgent) {
if (!rcvUrgent) {
RcvUrgentBegin();
HandleUrgentBegin();
}
notifUrgent = FALSE;
}
// tell stream owner about other notifications, TurboTCP doesn’t respond to these
if (notifTimeout) {
HandleTimeout();
notifTimeout = FALSE;
}
if (notifDataArrived) {
HandleUnexpectedData();
notifDataArrived = FALSE;
}
if (notifICMP) {
HandleICMP(¬ifICMPreport);
notifICMP = FALSE;
}
}
/*______________________________________________________________________
**
** ProcessAsyncCompletion
**
** Respond to the completion of an asynchronous call. Removes the async call object from
** the list of outstanding calls.
**
** theCall (CTCPAsyncCall *): the call which was completed
**
*/
void CTCPStream::ProcessAsyncCompletion (CTCPAsyncCall *theCall)
{
itsAsyncCalls->CCluster::Remove(theCall);
}
/***********************************************************************
************************************************************************
**
** INTERRUPT-LEVEL routines follow. These routines cannot make memory allocations, cannot make
** synchronous TCP calls, and cannot use the Think C profiler.
**
*/
#pragma options(!profile)
/*______________________________________________________________________
**
** PostponeDispose
**
** Add this stream object to the delayed disposal queue. This routine may be called during
** an interrupt.
**
*/
void CTCPStream::PostponeDispose (void)
{
if (pendingDispose) // ignore this if we’re already in disposal queue
return;
pendingDispose = TRUE;
disposeOnTerminate = TRUE;
Enqueue((QElemPtr) &qDisposeEntry, &(gTCPDriver->asyncQueue));
}
/*______________________________________________________________________
**
** PostponeNotify (protected method)
**
** The asynchronous notification routine (ASR). Receives notification of events for all TCP
** streams. Dispatches the notification to the proper method of the CTCPStream object. This
** routine is installed for all TCP streams created by the CTCPStream object.
**
** This method merely logs the kind of notification and adds this stream to the list of streams
** to be processed next time through the event loop.
**
** eventCode (b_16): event code (see MacTCP manual, p37)
** terminReason (b_16): reason for termination (if applicable)
** icmpMsg (structICMPReport *): ICMP report (if applicable)
**
*/
void CTCPStream::PostponeNotify (b_16 eventCode, b_16 terminReason, struct ICMPReport *icmpMsg)
{
// reject notifications for unexpected data if auto-receiving
if ((itsAutoReceiveSize > 0) && (eventCode == TCPDataArrival))
return;
// install this stream in asynchronous events queue
// CTCPDriver::ProcessNetEvents will pick it up later
if (!pendingNotify) {
Enqueue((QElemPtr) &qNotifyEntry, &(gTCPDriver->asyncQueue));
pendingNotify = TRUE;
}
// record the event type
switch (eventCode) {
case TCPClosing:
remoteClose = remoteClose || !pendingClose;
notifClosing = pendingClose = TRUE;
break;
case TCPULPTimeout:
notifTimeout = TRUE;
break;
case TCPTerminate:
notifTerminate = TRUE;
notifTermReason = terminReason;
break;
case TCPDataArrival:
notifDataArrived = TRUE;
break;
case TCPUrgent:
notifUrgent = TRUE;
break;
case TCPICMPReceived:
notifICMP = TRUE;
BlockMove(icmpMsg, ¬ifICMPreport, 24);
break;
}
}
/*______________________________________________________________________
**
** NotifyProc (static protected method)
**
** The asynchronous notification routine (ASR). This static method just decodes the
** userDataPtr and calls PostponeNotify for the object named. This routine is the standard
** ASR for all TCP streams created by the CTCPStream object.
**
** tcpStream (StreamPtr): the TCP stream in question
** eventCode (short): TCP event code (see MacTCP Dev guide, p37)
** userDataPtr (Ptr): user data pointer (in this case, the CTCPStream)
** terminReason (short): reason for termination (see MacTCP Dev guide, p37)
** icmpMsg (…): ICMP report (if applicable)
**
*/
pascal void CTCPStream::NotifyProc (StreamPtr tcpStream, unsigned short eventCode,
Ptr userDataPtr, unsigned short terminReason,
struct ICMPReport *icmpMsg)
{
CTCPStream *theTCPStream = (CTCPStream *) userDataPtr;
theTCPStream->PostponeNotify(eventCode, terminReason, icmpMsg);
}